package com.seashell.appstartupjob.config.parser;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.seashell.appstartupjob.config.node.CommonNode;
import com.seashell.appstartupjob.exceptions.JobConfigNotCorrectException;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.seashell.appstartupjob.job.IJob;

/**
 * com.seashell.appstartupjob.config.parser.JobConfigurationRootParser
 * 配置文件解析类
 *
 * @author Allen
 * @version v1.0
 * @date 2017/7/14
 */
public class JobConfigurationRootParser {

    public List<IJob> parse(File configFile) throws JobConfigNotCorrectException {
        List<IJob> targetList = new ArrayList<>();

        SAXReader reader = new SAXReader();
        try {
            //读取xml文件
            Document document = reader.read(configFile);
            Element root = document.getRootElement();
            Element jobsElement = root.element(CommonNode.JOBS_NODE_NAME);
            if(jobsElement==null){
                throw new JobConfigNotCorrectException("没有配置必要节点:"+CommonNode.JOBS_NODE_NAME);
            }
            Iterator<Element> jobsIterator = jobsElement.elementIterator(CommonNode.JOB_NODE_NAME);
            while (jobsIterator.hasNext()){
                Element job = jobsIterator.next();
                Element nameElement = job.element(CommonNode.JOB_NAME);
                Element jobConfigParserElement = job.element(CommonNode.JOB_CONFIG_PARSER);
                /**
                 * job类型解析
                 */
                Class<?> jobClass= parseJobType(job);
                /**
                 * job名称解析
                 */
                String jobName = parseJobName(nameElement);

                /**
                 * 实例化Job
                 */
                IJob jobInstance = instantiatesJob(jobClass,jobConfigParserElement,job,jobName);

                targetList.add(jobInstance);
            }

        } catch (DocumentException e) {
            throw new JobConfigNotCorrectException("文档结构不符合xml要求:" + configFile.getName());
        } catch (NoSuchMethodException e) {
            throw new JobConfigNotCorrectException("Job类不包含可正常实例化的构造方法");
        } catch (IllegalAccessException e) {
            throw new JobConfigNotCorrectException("Job类不可访问:"+e.getMessage());
        } catch (InstantiationException | InvocationTargetException e) {
            throw new JobConfigNotCorrectException("实例化Job出错:"+e.getMessage());
        } catch (ClassNotFoundException e) {
            throw new JobConfigNotCorrectException("未找到类型:"+e.getMessage());
        }

        return targetList;
    }

    public List<IJob> parse(String configFilePath) throws JobConfigNotCorrectException{
        File configFile = new File(configFilePath);
        if(!configFile.exists()){
            throw new JobConfigNotCorrectException("未知文件没找到:"+configFilePath);
        }
        return parse(configFile);
    }

    /**
     * 解析Job类型
     * @param job job节点
     * @return
     * @throws ClassNotFoundException
     */
    private Class<?> parseJobType(Element job) throws ClassNotFoundException {
        Element jobClassElement = job.element(CommonNode.JOB_CLASS_NAME);
        if(jobClassElement==null){
            throw new JobConfigNotCorrectException("没有配置工作类型");
        }
        String className = jobClassElement.getStringValue();
        Class<?> jobClass = Class.forName(className);
        if(!IJob.class.isAssignableFrom(jobClass)){
            throw new JobConfigNotCorrectException("没有配置正确的工作类型:"+className+",工作类型应该实现IJob接口");
        }
        return jobClass;
    }

    /**
     * 解析JobName
     * @param nameElement 名称节点
     * @return
     */
    private String parseJobName(Element nameElement){
        String jobName;
        if(StringUtils.isEmpty(nameElement.getStringValue())){
            jobName = null;
        }else{
            jobName = nameElement.getStringValue();
        }
        return jobName;
    }

    /**
     * 实例化Job类
     * @param jobClass job类型
     * @param jobConfigParserElement parser节点
     * @param job job节点
     * @param jobName job名称
     * @return
     * @throws NoSuchMethodException
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws InstantiationException
     */
    private IJob instantiatesJob(Class<?> jobClass,Element jobConfigParserElement,Element job,String jobName) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
        IJob jobInstance;
        if(jobConfigParserElement == null){
            Constructor<?> jobClassDeclaredConstructor = jobClass.getDeclaredConstructor(new Class[]{String.class,Element.class});
            jobClassDeclaredConstructor.setAccessible(true);
            jobInstance = (IJob)jobClassDeclaredConstructor.newInstance(new Object[]{jobName,job});
        }else{
            //获取parser类型
            Class<?> parserClass = Class.forName(jobConfigParserElement.getStringValue());

            Constructor<?> jobClassDeclaredConstructor = jobClass.getDeclaredConstructor(new Class[]{String.class,Class.class,Element.class});
            jobClassDeclaredConstructor.setAccessible(true);
            jobInstance = (IJob)jobClassDeclaredConstructor.newInstance(new Object[]{jobName,parserClass,job});
        }
        return jobInstance;
    }
}
